////////////////////////////////////////////////////////////////////////////////
//
//  FLYING KIWI BV
//  Copyright 2010 Flying Kiwi BV
//  All Rights Reserved.
//
//  NOTICE: Flying Kiwi permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package plantool.modules.users {
	//--------------------------------------------------------------------------
	//  Imports
	//--------------------------------------------------------------------------
	import flash.events.Event;
	import flash.utils.describeType;
	import flash.utils.getTimer;
	
	import kiwi.utils.Validation;
	
	import plantool.core.CoreApi;
	import plantool.data.Field;
	import plantool.data.FieldTypes;
	import plantool.data.QueryErrorResult;
	import plantool.data.QueryResult;
	import plantool.data.Table;
	import plantool.events.UserEvent;
	import plantool.events.UserHourEvent;
	import plantool.modules.menu.data.MenuGroupData;
	import plantool.modules.menu.data.MenuItemData;
	import plantool.modules.users.data.User;
	import plantool.modules.users.data.UserHourData;
	import plantool.modules.users.data.UserHourTypes;
	import plantool.modules.users.windows.UserListWindow;
	import plantool.modules.users.windows.UserWindow;
	
	//--------------------------------------------------------------------------
	//  [Metadata (Events, Style, Effects etc)]
	//--------------------------------------------------------------------------
	[Event(name="user_updated", type="plantool.events.UserEvent")]
	[Event(name="user_hour_updated", type="plantool.events.UserHourEvent")]
	
	//--------------------------------------------------------------------------
	//  Class declaration
	//--------------------------------------------------------------------------
	public class UserApi extends CoreApi {
		//--------------------------------------------------------------------------
		//Class constants
		//--------------------------------------------------------------------------
		private static const NUM_TABLES:uint				= 2;
		
		//--------------------------------------------------------------------------
		//Class variables
		//--------------------------------------------------------------------------
		private static var _instance:UserApi	= null;
		private var _hoursTable:Table			= null;
		private var _isWorking:Boolean			= false;
		private var _tblRdyCount:uint			= 0;
		private var _userTable:Table			= null;
		private var _userClass:Class			= User;
		private var _users:Vector.<User>		= new Vector.<User>();
		
		//--------------------------------------------------------------------------
		//Class properties
		//--------------------------------------------------------------------------
		public function get userClass():Class {
			return _userClass;
		}
		public function set userClass(value:Class):void {
			_userClass = value;
		}
		
		public function get userTable():Table {
			return _userTable;
		}
		
		//--------------------------------------------------------------------------
		//Constructor and Destructor
		//--------------------------------------------------------------------------
		public function UserApi(pvt:PrivateConstructorClass) {
			super();
			
			//create user-table fields
			_userTable = new Table("users");
			_userTable.addField(new Field("username"	, FieldTypes.VARCHAR));
			_userTable.addField(new Field("password"	, FieldTypes.VARCHAR));
			_userTable.addField(new Field("level"		, FieldTypes.INT));
			_userTable.addField(new Field("sort"		, FieldTypes.INT));
			_userTable.addField(new Field("color"		, FieldTypes.INT));
			
			//create user-hours fields
			_hoursTable	= new Table("user_hours");
			_hoursTable.addField(new Field("user_id"			, FieldTypes.INT));
			_hoursTable.addField(new Field("timestamp_from"		, FieldTypes.DATE));
			_hoursTable.addField(new Field("timestamp_till"		, FieldTypes.DATE));
			_hoursTable.addField(new Field("type"				, FieldTypes.VARCHAR));
			_hoursTable.addField(new Field("related_id"			, FieldTypes.INT));
			
			//add menu
			var group:MenuGroupData = new MenuGroupData();
			group.title				= "Medewerkers";
			group.addItem(new MenuItemData("Overzicht", showListForm));
			group.addItem(new MenuItemData("Toevoegen", showForm));
			menuApi.addGroup(group);
		}
		
		//--------------------------------------------------------------------------
		//Class methods
		//--------------------------------------------------------------------------
		public static function getInstance():UserApi {
			//check if instance exists, if not create one
			if (!Validation.isSet(_instance)) {
				_instance = new UserApi(new PrivateConstructorClass());
			}
			
			//return instance
			return _instance;
		}
		
		private function createUserHourQuery(user:User, from:Date, till:Date, type:String, related_id:uint):String {
			return "INSERT INTO `user_hours`(`user_id`, `type`, `timestamp_from`, `timestamp_till`, `related_id`) " +
				"VALUES(" + 
					user.id + ", '" + 
					type + "', " + 
					databaseManager.formatDate(from) + ", " + 
					databaseManager.formatDate(till) + ", " + 
					related_id.toString() + 
				")";
		}
		
		private function createUserHour(user:User, from:Date, till:Date, type:String, related_id:uint, onCompleteHandler:Function, onErrorHandler:Function, userdata:* = null):void {
			//create onCompleteHandler
			var onComplete_createHour:Function = function(result:QueryResult):void {
				//return to caller
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler(result);
				}
			}
			
			//create user hour record
			databaseManager.query(createUserHourQuery(user, from, till, type, related_id), onComplete_createHour, onErrorHandler, userdata);
		}
		
		private function createUser(user:User, onCompleteHandler:Function, onErrorHandler:Function):void {
			//TODO: Validate isWorking
			//create var for storing userid
			var newUserID:int = 0;
			
			//complete getting new users
			var onComplete_getNewUsers:Function = function(result:QueryResult):void {
				//pass result to complete handler
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler(result);
				}
				
				//inform that we're updated
				dispatchEvent(new UserEvent(UserEvent.USER_UPDATED, newUserID));
			}
			
			//create complete handler
			var onComplete_createUser:Function = function(result:QueryResult):void {
				//store new user id
				newUserID = result.insertRowId;
				
				//get new users
				getUsers(onComplete_getNewUsers, onErrorHandler);
			}
			
			//get all field to save to database
			var varList:XMLList = flash.utils.describeType(user)..accessor;
			
			//create the insert query
			//TODO:Generalize the creation insert-querys
			var fields:String	= "(";
			var values:String	= " VALUES(";
			var cnt:uint		= 0;
			for (var i:uint = 0; i < varList.length(); i++) {
				//skip the field 'id'
				if (varList[i].@name != "id") {
					//add field separator
					if (cnt > 0) {
						fields += ", ";
						values += ", ";
					}
					
					fields += "`" + varList[i].@name + "`";
					values += "'" + user[varList[i].@name] + "'";
					
					//increase field counter
					cnt++;
				}
			}
			
			//close query values and fields
			fields += ")";
			values += ")";
			
			//create query
			var query:String = "INSERT INTO `users`" + fields + values;

			//create the user
			databaseManager.query(query, onComplete_createUser, onErrorHandler);
		}
		
		private function initSettings():void {
		}
		
		private function onComplete_createTable():void {
			//table ready
			_tblRdyCount++;
			
			//all tables ready?
			if (_tblRdyCount == NUM_TABLES) {
				//init settings
				initSettings();
				
				//inform listeners that we're complete
				dispatchEvent(new Event(Event.COMPLETE));
			}
		}

		private function updateUser(user:User, onCompleteHandler:Function, onErrorHandler:Function):void {
			//TODO: Validate isWorking
			//complete getting new users
			var onComplete_getNewUsers:Function = function(result:QueryResult):void {
				//pass result to complete handler
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler(result);
				}
				
				//inform that we're updated
				dispatchEvent(new UserEvent(UserEvent.USER_UPDATED, user.id));
			}
			
			//create complete handler
			var onComplete_updateUser:Function = function(result:QueryResult):void {
				//get new users
				getUsers(onComplete_getNewUsers, onErrorHandler);
			}
			
			//get all field to save to database
			var varList:XMLList = flash.utils.describeType(user)..accessor;
			
			//create the update query
			//TODO:Generalize the creation update-querys
			var values:String	= "";
			var cnt:uint		= 0;
			for (var i:uint = 0; i < varList.length(); i++) {
				//skip the field 'id'
				if (varList[i].@name != "id") {
					//add field separator
					if (cnt > 0) {
						values += ", ";
					}
					
					//set values
					values += "`" + varList[i].@name + "`='" + user[varList[i].@name] + "'";
					
					//increase field counter
					cnt++;
				}
			}
			
			//create query
			var query:String = "UPDATE `users` SET " + values + " WHERE `id`=" + user.id;
			
			//create the user
			databaseManager.query(query, onComplete_updateUser, onErrorHandler);
		}
		
		public override function install():void {
			//check database
			databaseManager.createTable(_userTable, onComplete_createTable);
			databaseManager.createTable(_hoursTable, onComplete_createTable);
		}
		
		public function getAllUserHours(from:Date, till:Date, type:String, onCompleteHandler:Function, onErrorHandler:Function):void {
			//select all
			if (type == UserHourTypes.ALL) {
				databaseManager.query("SELECT `id`, `user_id`, `type`, `related_id`, " + databaseManager.formatDateField("`timestamp_from`") + " as `from`, " + databaseManager.formatDateField("`timestamp_till`") + " as `till` FROM `user_hours` WHERE (`from` >= " + databaseManager.formatDateField(databaseManager.formatDate(from)) + " AND `till` <= " + databaseManager.formatDateField(databaseManager.formatDate(till)) + ")", onCompleteHandler, onErrorHandler, UserHourData, [from, till]);
			} else {
				//select by type
				databaseManager.query("SELECT `id`, `user_id`, `type`, `related_id`, " + databaseManager.formatDateField("`timestamp_from`") + " as `from`, " + databaseManager.formatDateField("`timestamp_till`") + " as `till` FROM `user_hours` WHERE `type`='" + type + "' AND (`from` >= " + databaseManager.formatDateField(databaseManager.formatDate(from)) + " AND `till` <= " + databaseManager.formatDateField(databaseManager.formatDate(till)) + ")", onCompleteHandler, onErrorHandler, UserHourData,[from, till]);
			}
		}
		
		public function getAllUserHoursOnRelatedId(userId:uint, relatedId:uint, from:Date, till:Date, type:String, onCompleteHandler:Function, onErrorHandler:Function):void {
			if (type == UserHourTypes.ALL) {
				databaseManager.query("SELECT `id`, `user_id`, `type`, `related_id`, " + databaseManager.formatDateField("`timestamp_from`") + " as `from`, " + databaseManager.formatDateField("`timestamp_till`") + " as `till` FROM `user_hours` WHERE `user_id`=" + userId.toString() + " AND `related_id`=" + relatedId.toString() + " AND (`from` >= " + databaseManager.formatDateField(databaseManager.formatDate(from)) + " AND `till` <= " + databaseManager.formatDateField(databaseManager.formatDate(till)) + ")", onCompleteHandler, onErrorHandler, UserHourData);
			} else {
				databaseManager.query("SELECT `id`, `user_id`, `type`, `related_id`, " + databaseManager.formatDateField("`timestamp_from`") + " as `from`, " + databaseManager.formatDateField("`timestamp_till`") + " as `till` FROM `user_hours` WHERE `user_id`=" + userId.toString() + " AND `related_id`=" + relatedId.toString() + " AND `type`='" + type + "' AND (`from` >= " + databaseManager.formatDateField(databaseManager.formatDate(from)) + " AND `till` <= " + databaseManager.formatDateField(databaseManager.formatDate(till)) + ")", onCompleteHandler, onErrorHandler, UserHourData);
			}
		}
		
		public function getUserById(id:uint):User {
			//var to store user
			var user:User = null;
			
			//filter function to get user
			var filter:Function = function(item:User, idx:uint, list:Vector.<User>):Boolean {
				return (item.id == id);
			}
			
			//filter userlist to get user
			var list:Vector.<User> = _users.filter(filter);
			if (list.length > 0) {
				user = list[0];
			}
			
			//validate valid user
			if (!Validation.isSet(user)) {
				trace("No user with id ", id, "found. Users loaded with getUsers?");
			}
			
			//return nothing
			return user;
		}
		
		public function getUserHours(userid:uint, from:Date, till:Date, type:String, onCompleteHandler:Function, onErrorHandler:Function):void {
			//select all
			if (type == UserHourTypes.ALL) {
				databaseManager.query("SELECT `id`, `user_id`, `type`, `related_id`, " + databaseManager.formatDateField("`timestamp_from`") + " as `from`, " + databaseManager.formatDateField("`timestamp_till`") + " as `till` FROM `user_hours` WHERE `user_id`=" + userid.toString() + " AND (`from` >= " + databaseManager.formatDateField(databaseManager.formatDate(from)) + " AND `till` <= " + databaseManager.formatDateField(databaseManager.formatDate(till)) + ")", onCompleteHandler, onErrorHandler, UserHourData, [userid, from, till, type]);
			} else {
			//select by type
				databaseManager.query("SELECT `id`, `user_id`, `type`, `related_id`, " + databaseManager.formatDateField("`timestamp_from`") + " as `from`, " + databaseManager.formatDateField("`timestamp_till`") + " as `till` FROM `user_hours` WHERE `type`='" + type + "' AND `user_id`=" + userid.toString() + " AND (`from` >= " + databaseManager.formatDateField(databaseManager.formatDate(from)) + " AND `till` <= " + databaseManager.formatDateField(databaseManager.formatDate(till)) + ")", onCompleteHandler, onErrorHandler, UserHourData, [userid, from, till, type]);
			}
		}
		
		public function getUsers(onCompleteHandler:Function, onErrorHandler:Function):void {
			//create oncomplete function to store users
			var onGet_users:Function = function(result:QueryResult):void {
				if (Validation.isSet(result) && Validation.isSet(result.data) && result.data.length > 0) {
					//reset users
					_users.length = 0;
					
					//store users in vector
					for (var i:uint = 0; i < result.data.length; i++) {
						//store new loaded user
						_users.push((result.data[i] as User));
					}
				}
				
				//call oncomplete handler with result
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler(result);
				}
			}
			
			//get all users
			databaseManager.query("SELECT * FROM `users` ORDER BY `sort`", onGet_users, onErrorHandler, _userClass);
		}
		
		public function removeUser(user:User, onCompleteHandler:Function, onErrorHandler:Function):void {
			//complete getting new users
			var onComplete_getNewUsers:Function = function(result:QueryResult):void {
				//pass result to complete handler
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler(result);
				}
				
				//inform that we're updated
				dispatchEvent(new UserEvent(UserEvent.USER_UPDATED, user.id));
			}
			
			//create complete handler
			var onComplete_removeUser:Function = function(result:QueryResult):void {
				//get new users
				getUsers(onComplete_getNewUsers, onErrorHandler);
			}
				
			//delete the user
			//TODO: Remove all user-related data
			databaseManager.query("DELETE FROM `users` WHERE `id`=" + user.id, onComplete_removeUser, onErrorHandler);
		}
		
		public function removeUserHours(hourdatas:Array, onCompleteHandler:Function, onErrorHandler:Function, needUpdate:Boolean = true):void {
			//create onCompleteHandler
			var onComplete_deleteHour:Function = function(result:QueryResult):void {
				//check if we need to dispatch events
				if (needUpdate) {
					//dispatch event to update hours
					var sdate:Date = new Date(9999, 1);
					var edate:Date = new Date(1970, 1);
					
					for (var i:uint = 0; i < hourdatas.length; i++) {
						if (hourdatas[i].fromDate.valueOf() < sdate.valueOf()) sdate = hourdatas[i].fromDate;
						if (hourdatas[i].tillDate.valueOf() > edate.valueOf()) edate = hourdatas[i].tillDate;
					}
					dispatchEvent(new UserHourEvent(UserHourEvent.USER_HOUR_UPDATED, hourdatas[0].user_id, sdate, edate));
				}
				
				//call oncomplete
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler(result);
				}
			}
			
			//create user hour record
			var ids:String = "(";
			for (var j:uint = 0; j < hourdatas.length; j++) {
				ids += hourdatas[j].id.toString();
				
				(j < hourdatas.length -1) ? ids += ", " : ids += ")";
			}
			databaseManager.query("DELETE FROM `user_hours` WHERE `id` IN " + ids, onComplete_deleteHour, onErrorHandler);
		}
		
		public function saveUser(user:User, onCompleteHandler:Function, onErrorHandler:Function):void {
			//TODO: Validate isWorking
			//if id > 0 update the user, otherwise create a new user
			if (user.id > 0) {
				updateUser(user, onCompleteHandler, onErrorHandler);
			} else {
				createUser(user, onCompleteHandler, onErrorHandler);
			}
		}
		
		public function saveUserHour(user:User, from:Date, till:Date, type:String, related_id:uint, onCompleteHandler:Function, onErrorHandler:Function, userdata:* = null):void {
			//create onCompleteHandler
			var onComplete_createHour:Function = function(result:QueryResult):void {
				//unset isWorking
				_isWorking = false;
				
				//dispatch event to update hours
				dispatchEvent(new UserHourEvent(UserHourEvent.USER_HOUR_UPDATED, user.id, from, till));
				
				//return to caller
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler(result);
				}
			}
			
			//create the user hour
			createUserHour(user, from, till, type, related_id, onComplete_createHour, onErrorHandler, userdata);
		}
		
		public function saveUserHours(user:User, from:Date, till:Date, type:String, related_id:uint, skipHours:Array, onCompleteHandler:Function, onErrorHandler:Function, userdata:* = null):void {
			//if were not working
			if (!_isWorking) {
				//set isworking
				_isWorking = true;
				
				//duplicate date to avoid editing supplied date
				var sDate:Date			= new Date(from);
				var eDate:Date			= new Date(till);
				
				//continue untill startDate equals endDate
				while (sDate.valueOf() <= eDate.valueOf()) {
					//for this day, continue intill hours are equal
					while (sDate.hours < eDate.hours) {
						//increase hour
						sDate.hours += 1;
					}
					//increase day and reset hours
					sDate.date += 1;
					sDate.hours = from.hours;
				}
				
				//create onCompleteHandler for completing a create record
				var onComplete_createHour:Function = function(result:QueryResult):void {
					//unset isWorking
					_isWorking = false;
					
					//dispatch event to update hours
					dispatchEvent(new UserHourEvent(UserHourEvent.USER_HOUR_UPDATED, user.id, from, till));
					
					//call oncomplete
					if (Validation.isSet(onCompleteHandler)) {
						onCompleteHandler(result);
					}
				}
				
				var onError_createHour:Function = function(result:QueryErrorResult):void {
					trace("-- create userhours error --");
					//unset isWorking
					_isWorking = false;
					
					//call oncomplete
					if (Validation.isSet(onErrorHandler)) {
						onErrorHandler(result);
					}
				}
					
				//create an array to store the querys for the transaction
				var querys:Array = new Array();
				
				//create the hours
				sDate = new Date(from);
				//continue untill startDate equals endDate
				while (sDate <= eDate) {
					//for this day, continue intill hours are equal
					while (sDate.hours < eDate.hours) {
						//create till date
						var tillDate:Date 	= new Date(sDate);
						tillDate.hours		+= 1;
						
						//check for skipHours
						var skip:Boolean = false;
						if (Validation.isSet(skipHours)) {
							for (var i:uint = 0; i < skipHours.length; i++) {
								var h:UserHourData = skipHours[i] as UserHourData;
								if (Validation.isSet(h)) {
									if (h.fromDate.valueOf() == sDate.valueOf() && h.tillDate.valueOf() == tillDate.valueOf()) {
										skip = true;
										skipHours.splice(i, 1);
										break;
									}
								}
							}
						}
						
						//create user hours
						//createUserHour(user, sDate, tillDate, type, related_id, onComplete_createHour, onErrorHandler);
						if (!skip) {
							querys.push(createUserHourQuery(user, sDate, tillDate, type, related_id));
						}
						
						//go to next hour
						sDate 		= new Date(sDate);
						sDate.hours += 1;
					}
					
					//increase day and reset hours
					sDate.date += 1;
					sDate.hours = from.hours;
				}
				
				//execute the transaction
				databaseManager.transaction(querys, onComplete_createHour, onError_createHour, userdata);
			} else {
				trace("!!we're working!!");
			}
		}
		
		public function saveSorting(onCompleteHandler:Function, onErrorHandler:Function):void {
			//TODO: Validate isWorking
			var numUpdated:uint = 0;
			var onComplete_updateUser:Function = function(result:QueryResult):void {
				numUpdated++;
				if (numUpdated == _users.length) {
					if (Validation.isSet(onCompleteHandler)) {
						onCompleteHandler();
					}
				}
			}
			
			for (var i:uint = 0; i < _users.length; i++) {
				databaseManager.query("UPDATE `users` SET `sort`=" + _users[i].sort + " WHERE `id`=" + _users[i].id, onComplete_updateUser, onErrorHandler);
			}
		}
		
		public function updateUserHour(hourData:UserHourData, onCompleteHandler:Function = null, onErrorHandler:Function = null):void {
			var onComplete_updateUserHour:Function = function(result:QueryResult):void {
				//dispatch event
				dispatchEvent(new UserHourEvent(UserHourEvent.USER_HOUR_UPDATED, hourData.user_id, hourData.fromDate, hourData.tillDate));
				
				//call oncomplete
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler(result);
				}
			}
			databaseManager.query("UPDATE `user_hours` SET `related_id` = " + hourData.related_id + " WHERE `id` = " + hourData.id, onComplete_updateUserHour, onErrorHandler);
		}
		
		public function createWholeDayForUser(date:Date, relatedId:uint, user:User, type:String, invert:Boolean = true, onCompleteHandler:Function = null, onErrorHandler:Function = null):void {
			//TODO: Validate isWorking
			//check if type != all
			if (type == UserHourTypes.ALL) {
				throw new Error("UserApi::createWholeDayForUser - Cannot create a whole day for UserHourType::ALL");
				return;
			}
			
			//create vars for start and enddate
			var unAvailableHours:Array = new Array();
			var sdate:Date 	= new Date(date.valueOf());
			var edate:Date 	= new Date(sdate.valueOf());
			edate.hours		+= dateApi.hoursPerDay;
			
			//create a complete handler for removing all user hours already planned on the supplied day
			var onComplete_removeUserHours:Function = function(result:QueryResult):void {
				//create a whole day for the user
				userApi.saveUserHours(user, sdate, edate, type, relatedId, unAvailableHours, onCompleteHandler, onErrorHandler);
			}
				
			var onComplete_getUserHoursToRemove:Function = function(result:QueryResult):void {
				//remove the user hours already planned on this day. The whole day will be filled again afterwards
				if (Validation.isSet(result.data) && result.data.length > 0) {
					removeUserHours(result.data, onComplete_removeUserHours, onErrorHandler, false);
				} else {
					onComplete_removeUserHours(null);
				}
			}
			
			//create a complete-handler function
			var onComplete_getUserHours:Function = function(result:QueryResult):void {
				//validate userdata as UserDa
				if (Validation.isSet(result.data) && result.data.length > 0) {
					//check if the whole day is already planned
					if (result.data.length == dateApi.hoursPerDay - unAvailableHours.length) {
						//whole day is already planned, delete all hours if 'invert' is set
						if (invert) {
							removeUserHours(result.data, onCompleteHandler, onErrorHandler);
						}
					}
					//no whole day is planned. remove current hours and re-plan the whole day
					//TODO: do not remove current hours, only plan the new hours 
					else {
						//get hours to remove
						getUserHours(user.id, sdate, edate, type, onComplete_getUserHoursToRemove, onErrorHandler);
					}
				}
				//no hours found, insert the whole day
				else {
					//get hours to remove
					getUserHours(user.id, sdate, edate, type, onComplete_getUserHoursToRemove, onErrorHandler);
				}
			}
			
			//complete handler for free hours
			var onComplete_getFreeHours:Function = function(result:QueryResult):void {
				if (Validation.isSet(result.data) && result.data.length > 0) {
					unAvailableHours = result.data;
				}
				if (dateApi.hoursPerDay - unAvailableHours.length > 0) {
					getAllUserHoursOnRelatedId(user.id, relatedId, sdate, edate, type, onComplete_getUserHours, onErrorHandler);
				} else {
					trace('No available hours on this day');
				}
			}
				
			//first get all unavailable hours
			databaseManager.query("SELECT `id`, `user_id`, `type`, `related_id`, " + databaseManager.formatDateField("`timestamp_from`") + " as `from`, " + databaseManager.formatDateField("`timestamp_till`") + " as `till` FROM `user_hours` WHERE `type`!='" + type + "' AND `user_id`=" + user.id.toString() + " AND (`from` >= " + databaseManager.formatDateField(databaseManager.formatDate(sdate)) + " AND `till` <= " + databaseManager.formatDateField(databaseManager.formatDate(edate)) + ")", onComplete_getFreeHours, onErrorHandler, UserHourData, [user.id, sdate, edate, type]);
		}
		
		public function createWholeWeekForUser(relatedId:uint, user:User, type:String, invert:Boolean = true, onCompleteHandler:Function = null, onErrorHandler:Function = null):void {
			//TODO: Validate isWorking
			//check if type != all
			if (type == UserHourTypes.ALL) {
				throw new Error("UserApi::createWholeDayForUser - Cannot create a whole day for UserHourType::ALL");
				return;
			}
			
			//create vars for start and enddate
			var unAvailableHours:Array = new Array();
			var sdate:Date 	= dateApi.getStartDate();
			var edate:Date 	= new Date(sdate.valueOf());
			edate.date		+= dateApi.daysPerWeek - 1;
			edate.hours		+= dateApi.hoursPerDay;

			//create a complete handler for removing all user hours already planned on the supplied day
			var onComplete_removeUserHours:Function = function(result:QueryResult):void {
				//create a whole day for the user
				userApi.saveUserHours(user, sdate, edate, type, relatedId, unAvailableHours, onCompleteHandler, onErrorHandler, [sdate, edate, relatedId, user]);
			}
			
			var onComplete_getUserHoursToRemove:Function = function(result:QueryResult):void {
				//remove the user hours already planned on this day. The whole day will be filled again afterwards
				if (Validation.isSet(result.data) && result.data.length > 0) {
					removeUserHours(result.data, onComplete_removeUserHours, onErrorHandler, false);
				} else {
					onComplete_removeUserHours(null);
				}
			}
				
			//create a complete-handler function
			var onComplete_getUserHours:Function = function(result:QueryResult):void {
				//validate userdata as UserDa
				if (Validation.isSet(result.data) && result.data.length > 0) {
					//check if the whole day is already planned
					if (result.data.length == (dateApi.hoursPerDay * dateApi.daysPerWeek) - unAvailableHours.length) {
						//whole day is already planned, delete all hours if 'invert' is set
						if (invert) {
							removeUserHours(result.data, onCompleteHandler, onErrorHandler);
						}
					}
					//no whole day is planned. remove current hours and re-plan the whole day
					//TODO: do not remove current hours, only plan the new hours 
					else {
						//get hours to remove
						getUserHours(user.id, sdate, edate, type, onComplete_getUserHoursToRemove, onErrorHandler);
					}
				}
				//no hours found, insert the whole day
				else {
					//get hours to remove
					getUserHours(user.id, sdate, edate, type, onComplete_getUserHoursToRemove, onErrorHandler);
				}
			}
			
			//complete handler for free hours
			var onComplete_getFreeHours:Function = function(result:QueryResult):void {
				if (Validation.isSet(result.data) && result.data.length > 0) {
					unAvailableHours = result.data;
				}
				getAllUserHoursOnRelatedId(user.id, relatedId, sdate, edate, type, onComplete_getUserHours, onErrorHandler);
			}
				
			//first get all unavailable hours
			databaseManager.query("SELECT `id`, `user_id`, `type`, `related_id`, " + databaseManager.formatDateField("`timestamp_from`") + " as `from`, " + databaseManager.formatDateField("`timestamp_till`") + " as `till` FROM `user_hours` WHERE `type`!='" + type + "' AND `user_id`=" + user.id.toString() + " AND (`from` >= " + databaseManager.formatDateField(databaseManager.formatDate(sdate)) + " AND `till` <= " + databaseManager.formatDateField(databaseManager.formatDate(edate)) + ")", onComplete_getFreeHours, onErrorHandler, UserHourData, [user.id, sdate, edate, type]);
		}
		
		public function showForm(user:User = null):void {
			var window:UserWindow = new UserWindow();
			window.show();
		}
		
		public function showListForm():void {
			var window:UserListWindow = new UserListWindow();
			window.show();
		}
	}
}

//--------------------------------------------------------------------------
//END OF PACKAGE
//Define the PrivateConstructorClass here so an instance cannot be by anyone
//except for the class defined in the above package
//--------------------------------------------------------------------------
class PrivateConstructorClass {
	//--------------------------------------------------------------------------
	//Constructor and Destructor
	//--------------------------------------------------------------------------
	public function PrivateConstructorClass() {
	}
}